package com.micro.plugins.jwt.interceptor;

import com.alibaba.fastjson.JSON;
import com.micro.common.MicroConstant;
import com.micro.common.MicroResponseCode;
import com.micro.common.biz.TokenCacheKeyUtil;
import com.micro.core.exception.BizException;
import com.micro.core.handler.BaseContextHandler;
import com.micro.plugins.jwt.annotation.IgnoreUserAuth;
import com.micro.plugins.jwt.base.JWTInfoInterface;
import com.micro.plugins.jwt.configuration.JwtAuthConfiguration;
import com.micro.plugins.jwt.helper.ObjectHelper;
import com.micro.plugins.jwt.load.JwtLoadConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;


/**
 * <h1>用户请求拦截器</h1>
 * @author LevelCoder
 * @version V1.0.0
 * @email LevelCoder@126.com
 * @date 2020/5/11
 */
@Slf4j
@SuppressWarnings("ALL")
public class UserAuthRestInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private JwtLoadConfiguration jwtLoadConfiguration;

    @Autowired
    private JwtAuthConfiguration jwtAuthConfiguration;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
        /** 需要放行的请求地址 */
        log.info("请求地址",request.getRequestURI());
        if(jwtLoadConfiguration.getMicroJwt().getIgnoreUrlList().contains(request.getRequestURI())){
            return super.preHandle(request, response, handler);
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        // 配置该注解，说明不进行用户拦截
        IgnoreUserAuth annotation = handlerMethod.getBeanType().getAnnotation(IgnoreUserAuth.class);

        if (annotation == null){
            annotation = handlerMethod.getMethodAnnotation(IgnoreUserAuth.class);
        }

        if (annotation != null) {
            return super.preHandle(request, response, handler);
        }

//        log.info("请求IP >> requerstIP:{}", IpUtils.getIpAddr(request));
        log.info("请求路径 >> requerstURI:{}", request.getRequestURI());
        log.info("请求方式 >> requestMethod:{}", request.getMethod());
        log.info("请求参数 >> requestParams:{}", JSON.toJSONString(request.getParameterMap()));
        log.info("请求头token >> requestToken:{}", request.getHeader(jwtLoadConfiguration.getMicroJwt().getAccessTokenHeader()));
        log.info("请求参数post >> getMapsExt:{}", getMapsExt(request));

        /** 通过request 头 获取 token信息 */
        String token = request.getHeader(jwtLoadConfiguration.getMicroJwt().getAccessTokenHeader());
        String channelType = request.getHeader(jwtLoadConfiguration.getMicroJwt().getChannelHeader());

        /** 如果token 唯空 */
        if (ObjectHelper.isEmpty(token)){
            if (request.getCookies() != null){
                for (Cookie cookie : request.getCookies()) {
                    if (cookie.getName().equals(jwtLoadConfiguration.getMicroJwt().getAccessTokenHeader())) {
                        token = cookie.getValue();
                    }
                }
            }
        }

        /** 校验token是否为空 */
        if (ObjectHelper.isEmpty(token)){
            throw new BizException(MicroResponseCode.JWT_ILLEGAL_ARGUMENT.getResponseCode(), MicroResponseCode.JWT_ILLEGAL_ARGUMENT.getResponseMessage());
        }

        //从token中获取jwt信息
        JWTInfoInterface jwtInfoInterface = jwtAuthConfiguration.getInfoFromToken(token);

        log.info("请求头版本 >> requestVersion:{}", jwtInfoInterface.getServerVersion());
        log.info("请求头渠道 >> requestChannelType:{}", jwtInfoInterface.getChannelType());

        /** 校验请求来源 */
        if (!MicroConstant.ChannelType.channelTypeList.contains(jwtInfoInterface.getChannelType())){
            throw new BizException(MicroResponseCode.REQUEST_ORIGIN_ERROR.getResponseCode(),MicroResponseCode.REQUEST_ORIGIN_ERROR.getResponseMessage());
        }

        //根据token类型获取客户端类型
        String clientType = jwtAuthConfiguration.findClientType(jwtInfoInterface.getChannelType());

        /** 校验token有效期 */
        if(!stringRedisTemplate.hasKey(TokenCacheKeyUtil.getAccressTokenKey(jwtInfoInterface.getCustomerCode(),clientType))){
            throw new BizException(MicroResponseCode.JWT_TOKEN_EXPIRED.getResponseCode(),MicroResponseCode.JWT_TOKEN_EXPIRED.getResponseMessage());
        }
        /** sso 校验是否在其他设备登录 */

        if (!stringRedisTemplate.opsForValue().get(TokenCacheKeyUtil.getAccressTokenKey(jwtInfoInterface.getCustomerCode(),clientType)).equals(token)){
            throw new BizException(MicroResponseCode.AUTH_ACCOUNT_LANDING_ON_OTHER_DEVICES.getResponseCode(),MicroResponseCode.AUTH_ACCOUNT_LANDING_ON_OTHER_DEVICES.getResponseMessage());
        }

        //赋值到上下文
        BaseContextHandler.setCustomerCode(jwtInfoInterface.getCustomerCode());
        BaseContextHandler.setCustomerType(jwtInfoInterface.getCustomerType());
        BaseContextHandler.setCustomerName(jwtInfoInterface.getCustomerName());
        BaseContextHandler.setServerName(jwtInfoInterface.getServerName());
        BaseContextHandler.setChannelType(jwtInfoInterface.getChannelType());

        return super.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception{
        BaseContextHandler.remove();
        super.afterCompletion(request, response, handler, ex);
    }


    public static Map<String, Object> getMapsExt(HttpServletRequest req) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        Enumeration<String> enume = req.getParameterNames();
        while (enume.hasMoreElements()) {
            String key = (String) enume.nextElement();
            String[] values = req.getParameterValues(key);
            paramMap.put(key, values.length == 1 ? req.getParameter(key) : values);
        }
        return paramMap;
    }
}
